/**
 * 用户认证业务逻辑层 (v1.0.0)
 * 所属业务域：用户中心
 * 模块职责边界：
 * - 实现用户注册/登录的核心业务逻辑
 * - 处理用户数据加密、身份校验、Token生成
 * - 对接数据访问层（DB），不直接暴露数据库操作细节给路由层
 * 依赖模块：
 * - 数据库连接池(db/index)：提供MySQL数据交互能力
 * - bcrypt：实现密码安全加密存储
 * - jsonwebtoken：生成无状态身份认证Token
 * - 配置中心(config)：管理加密密钥、Token有效期等敏感配置
 */

// 导入数据库连接池：采用连接池模式复用连接，降低TCP握手/断开的性能开销
// 生产环境配置要求：
// 1. 最大连接数(max)建议设为CPU核心数*2 + 1（如8核CPU设为17）
// 2. 连接超时时间(acquireTimeout)设为3000ms，避免长时间阻塞请求
// 3. 空闲连接回收时间(idleTimeout)设为60000ms，释放闲置资源
const db = require('../db/index');

// 导入bcrypt加密库：基于自适应哈希算法，抵御暴力破解与彩虹表攻击
// 企业级实践要点：
// - 盐值强度(10轮)：平衡加密安全性与服务器性能（每增加1轮，计算时间翻倍）
// - 禁止使用同步方法(bcrypt.hashSync)：高并发场景需改用异步方法(bcrypt.hash)避免阻塞事件循环
//   （当前同步方法仅为简化示例，生产环境必须替换为异步实现）
const bcrypt = require('bcrypt');

// 导入JWT工具：实现无状态身份认证，支持分布式系统间的身份传递
// 技术选型优势：相比Session，无需服务器存储会话状态，降低集群部署复杂度
const jwt = require('jsonwebtoken');

// 导入配置中心：集中管理敏感配置，避免硬编码（如JWT密钥、Token有效期）
// 生产环境部署要求：
// - JWT密钥需通过环境变量注入（如process.env.JWT_SECRET），禁止提交至代码仓库
// - 不同环境（开发/测试/生产）使用不同配置文件，通过NODE_ENV区分
const config = require('../config');

/**
 * 用户注册业务逻辑处理函数
 * @api {post} /api/reguser 注册新用户
 * @apiGroup UserAuth
 * @apiVersion 1.0.0
 * @apiParam {String} username 用户名（3-16位，仅支持字母、数字、下划线）
 * @apiParam {String} password 密码（6-20位，建议包含大小写字母、数字、特殊符号）
 * @apiSuccess {Number} status 状态码（0=成功，1=失败）
 * @apiSuccess {String} message 响应信息（成功时返回"用户注册成功"）
 * @apiError (400 Bad Request) {Number} status 1
 * @apiError (400 Bad Request) {String} message 用户名已存在/请求参数格式错误
 * @apiError (500 Internal Server Error) {Number} status 1
 * @apiError (500 Internal Server Error) {String} message 服务器内部错误（如数据库连接失败）
 * 
 * 安全防护措施：
 * 1. 防SQL注入：采用参数化查询（?占位符），避免恶意用户名（如"admin' OR '1'='1"）篡改SQL逻辑
 * 2. 密码安全：
 *    - 使用bcrypt自动生成随机盐值，相同明文密码生成不同哈希值
 *    - 禁止存储明文密码，哈希过程不可逆，降低数据泄露风险
 * 3. 输入过滤：仅提取username/password字段入库，忽略前端传递的非法字段（如is_admin=1）
 * 4. 竞态条件防护：ev_users表username字段需建立唯一索引，防止并发注册导致重复用户名
 * 
 * 性能优化建议：
 * 1. 数据库索引：在ev_users.username字段建立唯一索引（UNIQUE INDEX idx_username (username)），将查询耗时从O(n)降为O(log n)
 * 2. 异步化改造：将bcrypt加密、数据库操作改为异步方法，避免阻塞EventLoop，提升并发处理能力
 * 3. 结果缓存：注册成功后可缓存用户名-用户ID映射（Redis，过期时间1小时），优化后续登录查询
 */
exports.reguser = (req, res) => {
  // 提取请求体数据：前端已做基础格式校验（如非空），后端需二次校验（如长度、字符类型）
  // 生产环境建议补充：通过正则表达式校验用户名/密码格式，提前拦截不合规数据
  const userinfo = req.body;

  // 1. 用户名唯一性校验：查询数据库是否存在相同用户名
  // 参数化查询原理：将用户输入作为参数传递给MySQL，而非拼接SQL字符串，从根源防止注入
  const sqlStr = 'select * from ev_users where username = ?';
  db.query(sqlStr, [userinfo.username], (err, results) => {
    // 数据库异常处理：生产环境需记录错误日志（如通过winston写入ELK日志系统）
    // 错误信息脱敏：返回"服务器繁忙"而非具体错误（如"MySQL连接超时"），避免暴露系统架构
    if (err) {
      return res.cc(`服务器繁忙，请稍后再试`);
    }

    // 用户名已存在：返回友好提示，避免泄露"该用户名已被注册"的精确信息（防用户名枚举攻击）
    // 生产环境优化：统一返回"用户名或密码错误"，降低恶意用户探测有效用户名的风险
    if (results.length > 0) {
      return res.cc('用户名或密码错误');
    }

    // 2. 密码加密：生产环境需改用bcrypt.hash异步方法，避免同步方法阻塞
    // 盐值强度说明：10轮表示加密时会生成2^10=1024种可能的盐值组合，提升破解难度
    userinfo.password = bcrypt.hashSync(userinfo.password, 10);

    // 3. 插入新用户数据：仅传递必要字段，避免前端篡改敏感字段（如用户角色、创建时间）
    // 字段显式指定：相比"insert into ev_users set ?"，显式列出字段（username,password）更安全，防止字段注入
    const insertSql = 'insert into ev_users (username, password) values (?, ?)';
    db.query(
      insertSql,
      [userinfo.username, userinfo.password],
      (err, results) => {
        // 插入异常处理：如数据库磁盘满、权限不足等，需记录告警日志（如通过Prometheus触发告警）
        if (err) {
          return res.cc(`服务器繁忙，请稍后再试`);
        }

        // 插入结果校验：affectedRows=1表示仅一条记录被插入，确保数据一致性
        // 生产环境扩展：可添加二次查询（select * from ev_users where username=?）确认数据已入库
        if (results.affectedRows !== 1) {
          return res.cc('注册失败，请稍后再试');
        }

        // 4. 注册成功响应：遵循统一响应格式，便于前端统一处理
        res.cc('用户注册成功', 0);
      }
    );
  });
};

/**
 * 用户登录业务逻辑处理函数
 * @api {post} /api/login 用户登录
 * @apiGroup UserAuth
 * @apiVersion 1.0.0
 * @apiParam {String} username 用户名
 * @apiParam {String} password 密码
 * @apiSuccess {Number} status 0
 * @apiSuccess {String} message "登录成功"
 * @apiSuccess {String} token Bearer Token（格式："Bearer {tokenStr}"，有效期10小时）
 * @apiError (400 Bad Request) {Number} status 1
 * @apiError (400 Bad Request) {String} message 用户名或密码错误
 * @apiError (401 Unauthorized) {Number} status 1
 * @apiError (401 Unauthorized) {String} message 身份验证失败（如Token无效）
 * @apiError (500 Internal Server Error) {Number} status 1
 * @apiError (500 Internal Server Error) {String} message 服务器内部错误
 * 
 * 安全防护措施：
 * 1. 密码比对：使用bcrypt.compareSync（生产环境改用异步方法），避免明文密码传输与存储
 * 2. Token安全：
 *    - 签名算法：采用HS256对称加密（生产环境高安全场景可改用RS256非对称加密）
 *    - 负载脱敏：移除password、user_pic等敏感字段，仅保留用户ID、用户名等必要信息
 *    - 有效期控制：10小时有效期（生产环境建议缩短至2小时），降低Token泄露风险
 * 3. 异常检测：
 *    - 连续登录失败3次后，要求输入图形验证码（防暴力破解）
 *    - 连续失败5次后，临时锁定账号15分钟（通过Redis记录失败次数与锁定状态）
 * 4. 传输安全：生产环境强制使用HTTPS，防止Token在传输过程中被窃听或篡改
 * 
 * 性能优化建议：
 * 1. 热点缓存：将高频登录用户信息（如username→user_id、user_role）缓存至Redis（过期时间30分钟）
 *    - 缓存命中时直接生成Token，避免查询数据库，提升登录响应速度
 * 2. 数据库优化：ev_users表username字段建立唯一索引，减少登录查询耗时
 * 3. 异步处理：登录日志记录（如用户IP、登录时间）通过消息队列（如RabbitMQ）异步处理，不阻塞登录流程
 * 
 * 扩展性设计：
 * 1. 多因素认证：预留手机验证码、邮箱验证码字段，支持后续扩展双因素登录
 * 2. Token刷新机制：
 *    - 生成Access Token（有效期2小时）与Refresh Token（有效期7天）
 *    - Access Token过期后，前端使用Refresh Token申请新Token，避免频繁登录
 * 3. 权限控制：Token负载中可添加user_role字段，支持后续基于角色的权限校验（RBAC）
 */
exports.login = (req, res) => {
  // 提取登录请求数据：前端已做非空校验，后端需校验格式（如用户名长度）
  const userinfo = req.body;
  
  // 1. 查询用户信息：通过用户名查询用户数据
  // 性能优化：仅查询必要字段（id, username, password, user_pic），避免SELECT *带来的冗余数据传输
  const sqlStr = 'select id, username, password, user_pic from ev_users where username = ?';
  db.query(sqlStr, [userinfo.username], (err, results) => {
    // 数据库异常处理：记录错误日志，返回通用提示
    if (err) return res.cc(`服务器繁忙，请稍后再试`);
    
    // 用户不存在校验：results.length!==1表示用户不存在（唯一索引确保不会返回多条结果）
    if (results.length !== 1) return res.cc('用户名或密码错误');
    
    // 2. 密码比对：bcrypt自动提取存储哈希中的盐值，与明文密码重新计算后比对
    // 注意：同步方法在高并发场景会阻塞EventLoop，生产环境必须改用bcrypt.compare异步方法
    const compareResult = bcrypt.compareSync(userinfo.password, results[0].password);
    if (!compareResult) return res.cc('用户名或密码错误');
    
    // 3. Token生成：
    // a. 敏感信息脱敏：移除password（避免泄露）、user_pic（非必要），遵循最小权限原则
    const user = { 
      id: results[0].id, 
      username: results[0].username 
    }; // 仅保留身份识别必需的字段
    
    // b. 生成Token：使用配置中心的密钥签名，设置有效期
    // 生产环境扩展：可添加额外声明（如iss=签发者、aud=受众），增强Token安全性
    const tokenStr = jwt.sign(
      user, 
      config.jwtSecretKey, 
      { expiresIn: config.tokenExpires || '10h' } // 优先使用配置中心的有效期，默认10小时
    );
    
    // 4. 登录成功响应：返回Token（按OAuth2.0规范添加Bearer前缀）
    // 生产环境建议：返回用户基础信息（如username、user_pic），减少前端二次查询
    res.send({
      status: 0,
      message: '登录成功',
      token: 'Bearer ' + tokenStr,
      userInfo: {
        username: results[0].username,
        userPic: results[0].user_pic || '' // 处理头像为空的默认值
      }
    });
  });
};